#include <stdio.h>
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "esp_vfs_fat.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "soc/soc_caps.h"
#include "cmd_system.h"
#include "cmd_wifi.h"
#include "cmd_nvs.h"
#include "cmd_open.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "md_shell.h"

// 定义日志标签和目标名称
static const char *TAG = "md_shell";
#define PROMPT_STR "MdLink"

// 添加全局变量用于控制shell任务
static TaskHandle_t shell_task_handle = NULL;
static SemaphoreHandle_t shell_mutex = NULL;
static bool shell_initialized = false;

/**
 * @brief 初始化控制台
 * 
 * 此函数配置UART驱动和控制台环境，包括：
 * 1. 设置行结束符
 * 2. 配置UART参数
 * 3. 安装UART驱动
 * 4. 初始化控制台
 * 5. 配置linenoise库（命令行编辑和历史功能）
 */
static void initialize_console(void)
{
    /* 在重新配置前清空stdout缓冲区 */
    fflush(stdout);
    fsync(fileno(stdout));

    /* 禁用stdin的缓冲 */
    setvbuf(stdin, NULL, _IONBF, 0);

    /* 设置UART接收行结束符为CR（回车） */
    /* Minicom、screen、idf_monitor在按下ENTER键时发送CR */
    esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
    /* 设置UART发送行结束符为CRLF（回车换行） */
    /* 在'\n'时将光标移动到下一行的开头 */
    esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);

    /* 配置UART参数 */
    /* 注意使用REF_TICK，以便在轻睡眠模式下APB频率变化时波特率保持正确 */
    const uart_config_t uart_config = {
        .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, // 波特率
        .data_bits = UART_DATA_8_BITS,                 // 数据位
        .parity = UART_PARITY_DISABLE,                 // 校验位
        .stop_bits = UART_STOP_BITS_1,                 // 停止位
#if SOC_UART_SUPPORT_REF_TICK
        .source_clk = UART_SCLK_REF_TICK, // 时钟源
#elif SOC_UART_SUPPORT_XTAL_CLK
        .source_clk = UART_SCLK_XTAL,
#endif
    };
    /* 安装UART驱动以支持中断驱动的读写 */
    ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM,
                                        256, 0, 0, NULL, 0));
    ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));

    /* 告诉VFS使用UART驱动 */
    esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);

    /* 初始化控制台 */
    esp_console_config_t console_config = {
        .max_cmdline_args = 8,     // 最大命令行参数数量
        .max_cmdline_length = 256, // 最大命令行长度
#if CONFIG_LOG_COLORS
        .hint_color = atoi(LOG_COLOR_CYAN) // 提示颜色
#endif
    };
    ESP_ERROR_CHECK(esp_console_init(&console_config));

    /* 配置linenoine行完成库 */
    /* 启用多行编辑。如果未设置，长命令将在单行内滚动 */
    linenoiseSetMultiLine(1);

    /* 告诉linenoise从哪里获取命令完成和提示 */
    linenoiseSetCompletionCallback(&esp_console_get_completion);
    linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint);

    /* 设置命令历史记录大小 */
    linenoiseHistorySetMaxLen(100);

    /* 设置命令最大长度 */
    linenoiseSetMaxLineLen(console_config.max_cmdline_length);

    /* 不允许返回空行 */
    linenoiseAllowEmpty(false);

#if CONFIG_STORE_HISTORY
    /* 从文件系统加载命令历史 */
    linenoiseHistoryLoad(HISTORY_PATH);
#endif
}

/**
 * @brief 反初始化控制台
 */
static void deinitialize_console(void)
{
    /* 保存命令历史 */
#if CONFIG_STORE_HISTORY
    linenoiseHistorySave(HISTORY_PATH);
#endif

    /* 释放linenoise资源 */
    linenoiseHistoryFree();

    /* 反初始化控制台 */
    esp_console_deinit();

    /* 卸载UART驱动 */
    esp_vfs_dev_uart_use_nonblocking(CONFIG_ESP_CONSOLE_UART_NUM);
    uart_driver_delete(CONFIG_ESP_CONSOLE_UART_NUM);
}

/**
 * @brief 应用程序主入口函数
 * 
 * 此函数是ESP32应用程序的入口点，负责：
 * 1. 初始化NVS存储
 * 2. 初始化文件系统（如果启用历史记录）
 * 3. 初始化控制台
 * 4. 注册各种命令
 * 5. 进入主循环处理用户输入
 */
void md_shell_task(void *arg)
{
    // 初始化控制台环境
    initialize_console();

    /* 注册命令 */
    esp_console_register_help_command(); // 注册帮助命令
    register_system_common();            // 注册系统常用命令
    register_system_sleep();             // 注册系统睡眠命令
#if CONFIG_ESP_WIFI_ENABLED
    register_wifi(); // 注册WiFi命令（如果启用WiFi）
#endif
    register_nvs(); // 注册NVS命令
    register_open(); // 注册open命令

    /* 定义提示符字符串 */
    /* 可以在每行前打印，可以自定义、动态变化等 */
    const char *prompt = LOG_COLOR_I PROMPT_STR "> " LOG_RESET_COLOR;


    vTaskDelay(pdMS_TO_TICKS(2300)); // 等待2秒，再打印欢迎信息
    /* 打印大大的MdLink ASCII艺术字 */
    printf("\n\n");
    printf("███╗   ███╗██████╗ ██╗     ██╗███╗   ██╗██╗  ██╗\n");
    printf("████╗ ████║██╔══██╗██║     ██║████╗  ██║██║ ██╔╝\n");
    printf("██╔████╔██║██║  ██║██║     ██║██╔██╗ ██║█████╔╝ \n");
    printf("██║╚██╔╝██║██║  ██║██║     ██║██║╚██╗██║██╔═██╗ \n");
    printf("██║ ╚═╝ ██║██████╔╝███████╗██║██║ ╚████║██║  ██╗\n");
    printf("╚═╝     ╚═╝╚═════╝ ╚══════╝╚═╝╚═╝  ╚═══╝╚═╝  ╚═╝\n");
    printf("\n\n");
    // 打印欢迎信息和使用说明
    printf("\n"
           "欢迎使用MdLink控制台！\n"
           "输入'help'获取命令列表。\n"
           "使用上/下箭头键浏览命令历史。\n"
           "输入命令时按TAB键自动完成。\n"
           "按Enter或Ctrl+C将终止控制台环境。\n");

    /* 检查终端是否支持转义序列 */
    int probe_status = linenoiseProbe();
    if (probe_status) { /* 0表示成功 */
        printf("\n"
               "您的终端应用程序不支持转义序列。\n"
               "行编辑和历史功能已禁用。\n"
               "在Windows上,尝试使用Putty代替。\n");
        linenoiseSetDumbMode(1); // 设置为哑模式
#if CONFIG_LOG_COLORS
        /* 由于终端不支持转义序列，提示符中不要使用颜色代码 */
        prompt = PROMPT_STR "> ";
#endif //CONFIG_LOG_COLORS
    }

    /* 主循环 */
    while (true) {
        /* 检查是否需要退出 */
        if (ulTaskNotifyTake(pdTRUE, 0)) {
            break;
        }

        /* 使用linenoise获取一行输入 */
        /* 当按下ENTER键时返回该行 */
        char *line = linenoise(prompt);
        if (line == NULL) { /* 在EOF或错误时中断 */
            continue;       // 继续循环而不是退出
        }
        /* 如果非空，将命令添加到历史记录 */
        if (strlen(line) > 0) {
            linenoiseHistoryAdd(line);
#if CONFIG_STORE_HISTORY
            /* 保存命令历史到文件系统 */
            linenoiseHistorySave(HISTORY_PATH);
#endif
        }

        /* 尝试运行命令 */
        int ret;
        esp_err_t err = esp_console_run(line, &ret);
        if (err == ESP_ERR_NOT_FOUND) {
            printf("未识别的命令\n");
        } else if (err == ESP_ERR_INVALID_ARG) {
            // 命令为空
        } else if (err == ESP_OK && ret != ESP_OK) {
            printf("命令返回非零错误代码: 0x%x (%s)\n", ret, esp_err_to_name(ret));
        } else if (err != ESP_OK) {
            printf("内部错误: %s\n", esp_err_to_name(err));
        }
        /* linenoise在堆上分配行缓冲区，需要释放 */
        linenoiseFree(line);
    }

    // 清理资源
    deinitialize_console();

    // 标记shell已停止
    xSemaphoreTake(shell_mutex, portMAX_DELAY);
    shell_initialized = false;
    shell_task_handle = NULL;
    xSemaphoreGive(shell_mutex);

    // 删除自身任务
    vTaskDelete(NULL);
}

void md_shell_init(void)
{
    // 创建互斥锁
    if (shell_mutex == NULL) {
        shell_mutex = xSemaphoreCreateMutex();
    }

    xSemaphoreTake(shell_mutex, portMAX_DELAY);

    // 如果shell已经初始化，则不再创建新任务
    if (shell_initialized) {
        xSemaphoreGive(shell_mutex);
        return;
    }
    // 创建shell任务
    if (xTaskCreate(md_shell_task, "md_shell_task", 4096 * 2, NULL, 5, &shell_task_handle) == pdPASS) {
        shell_initialized = true;
        ESP_LOGI(TAG, "Shell task started");
    } else {
        ESP_LOGE(TAG, "Failed to create shell task");
    }

    xSemaphoreGive(shell_mutex);
}

void md_shell_deinit(void)
{
    xSemaphoreTake(shell_mutex, portMAX_DELAY);

    if (shell_initialized && shell_task_handle != NULL) {
        // 通知shell任务退出
        xTaskNotifyGive(shell_task_handle);

        // 等待任务结束（最多等待1秒）
        for (int i = 0; i < 10 && shell_initialized; i++) {
            xSemaphoreGive(shell_mutex);
            vTaskDelay(100 / portTICK_PERIOD_MS);
            xSemaphoreTake(shell_mutex, portMAX_DELAY);
        }

        if (shell_initialized) {
            // 如果任务没有正常退出，强制删除
            vTaskDelete(shell_task_handle);
            shell_initialized = false;
            shell_task_handle = NULL;
            ESP_LOGW(TAG, "Shell task force deleted");
        } else {
            ESP_LOGI(TAG, "Shell task stopped gracefully");
        }
    }

    xSemaphoreGive(shell_mutex);
}